using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace DotNetCommon.Expressions.Base
{
    internal class BaseVisit
    {
        protected virtual string GenerateFullMarkString(ExpressionNode node) => throw new NotImplementedException();
        protected virtual void Prepare(ExpressionNode node) => throw new NotImplementedException();
        protected virtual Expression Rebuild(ExpressionNode node) => node.Expression;

        /// <summary>
        /// 遍历树节点入口
        /// </summary>
        /// <param name="node"></param>
        /// <param name="visit"></param>
        public void Visit(ExpressionNode node, Action<ExpressionNode> visit)
        {
            Prepare(node);
            node.Children.ForEach(x => visit(x));
            node.FullMarkString = GenerateFullMarkString(node);
            Reduce(node);
        }

        /// <summary>
        /// 简化节点方法
        /// </summary>
        /// <param name="node"></param>
        public void Reduce(ExpressionNode node)
        {
            //判断是否能简化
            if (node.Children.Count > 1)
            {
                var canReduce = false;
                for (int i = 0; i < node.Children.Count - 1; i++)
                {
                    if (node.Children[i].HasParameter ^ node.Children[i + 1].HasParameter)
                    {
                        //不相同 要进行简化
                        canReduce = true;
                        break;
                    }
                }
                if (canReduce)
                {
                    for (int i = 0; i < node.Children.Count; i++)
                    {
                        var child = node.Children[i];
                        if (!child.HasParameter && child.Expression != null)
                        {
                            if (child.NodeType != ExpressionType.Constant)
                            {
                                //进行简化
                                var res = child.Reduce();
                                var contant = Expression.Constant(res);
                                node.Children[i] = new ExpressionNode
                                {
                                    Parent = node,
                                    Expression = contant,
                                    _updateRequest = true
                                };
                            }
                        }
                    }
                }
            }
            if (node.NeedUpdate)
            {
                node.Expression = Rebuild(node);
            }
        }
    }
}
